home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-06-18 | 33.5 KB | 1,079 lines | [TEXT/MPS ] |
- ;
- ; DylanTalk by Dean Yu and Fred Monroe (President/Chief Technical Officer)
- ;
- ; Copyright 1992 FGM Enterprises. All rights reserved.
- ;
-
- include 'SysEqu.a'
- include 'QuickEqu.a'
- include 'ToolEqu.a'
- include 'Traps.a'
- include 'GestaltEqu.a'
- include 'Icons.a'
- include 'Sound.a'
-
- include 'DylanTalkEqu.a'
-
- ;_________________________________________________________________________________________
- ; Installation code
-
- InstallDylanTalk Proc Export
- Import DylanTalkGestalt
- Import ShowHidePatch, OldShowHide
- Import NewDialogPatch, oldNewDialog
- Import SystemTaskPatch
- Import SystemMenuPatch, oldSystemMenu
- Import HiliteControlPatch, oldHiliteControl
- Import TrackControlPatch, oldTrackControl
- Import DialogSelectPatch, oldDialogSelect
- Import OpenPersonalityFile
-
- tst.w $17a
- bpl.s @noCMDKey
- _Debugger
- @noCMDKey
-
- ; Look for our globals. If the 'DTlk' gestalt selector is already there, don’t install
- ; ourselves again.
-
- move.l a0,-(sp) ; Save handle to init
- move.l #gestaltDylanTalk,d0
- _Gestalt
- cmpi.w #gestaltDupSelectorErr,d0 ; See if globals are already installed
- bne.s @installDylanTalk ; Go ahead and install ourselves
- addq #4,sp ; Pop the handle
- rts
-
- @installDylanTalk
- _DetachResource ; Detach INIT so it sticks around.
-
- ; Install our main patch, which parses static text items when dialog windows appear.
-
- move.w #kShowHideTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldShowHide,a1
- move.l a0,2(a1)
- lea ShowHidePatch,a0
- move.w #kShowHideTrapWord,d0
- _SetTrapAddress ,NewTool ; Apply our patch to ShowHide
-
- ; Set up a patch on NewDialog which forces it to call ShowHide. That
- ; way we can catch alerts too…
-
- move.w #kNewDialogTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldNewDialog,a1
- move.l a0,2(a1)
- lea NewDialogPatch,a0
- move.w #kNewDialogTrapWord,d0
- _SetTrapAddress ,NewTool ; Apply our patch to NewDialog
-
- ; Install patch to HiliteControl which talks when the keyboard is used to dismiss a dialog
-
- move.w #kHiliteControlTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldHiliteControl,a1
- move.l a0,2(a1) ; Save real TrackControl
- lea HiliteControlPatch,a0
- move.w #kHiliteControlTrapWord,d0
- _SetTrapAddress ,NewTool
-
- ; Install patch to TrackControl which says “Enh” if mouse is let up outside of a control.
-
- move.w #kTrackControlTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldTrackControl,a1
- move.l a0,2(a1) ; Save real TrackControl
- lea TrackControlPatch,a0
- move.w #kTrackControlTrapWord,d0
- _SetTrapAddress ,NewTool
-
- ; Install patch to DialogSelect which takes care of speaking control titles when they
- ; are hit.
-
- move.w #kDialogSelectTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldDialogSelect,a1
- move.l a0,2(a1) ; Save real DialogSelect
- lea DialogSelectPatch,a0
- move.w #kDialogSelectTrapWord,d0
- _SetTrapAddress ,NewTool
-
- ; Set up patch to SystemTask to install our menu
-
- move.w #kSystemTaskTrapWord,d0
- _GetTrapAddress ,NewTool
- lea SystemTaskPatch,a1
- move.l a0,2(a1)
- move.l a1,a0
- move.w #kSystemTaskTrapWord,d0
- _SetTrapAddress ,NewTool ; Apply our patch to SystemTask
-
- ; Set up patch to SystemMenu which tracks selections in the personality menu
-
- move.w #kSystemMenuTrapWord,d0
- _GetTrapAddress ,NewTool
- lea oldSystemMenu,a1
- move.l a0,2(a1)
- lea SystemMenuPatch,a0
- move.w #kSystemMenuTrapWord,d0
- _SetTrapAddress ,NewTool ; Apply our patch to SystemMenu
-
- ; Allocate memory for our globals
-
- move.l #DylanTalkGlobals.globalSize,d0
- _NewHandle ,Sys,Clear
- _HLock
- move.l a0,a3 ; Handle to our globals
- move.l (a3),a2
-
- move.l #256,d0
- _NewPtr ,Sys,Clear
- move.l a0,DylanTalkGlobals.firstDialogString(a2) ; Space for string to speak
-
- ; Get our about box dialog.
-
- move.l TheZone,-(sp) ; Save the current zone
- move.l SysZone,TheZone ; Make the system zone the current zone
-
- subq #2,sp
- move.w #times,-(sp) ; I know I’m supposed to get the name, but as Fred says, “This is just a hack.”
- move.w #72,-(sp) ; See if the biggest size we use in the About box is available.
- _RealFont
- tst.b (sp)+ ; If the size is right, make our about box available.
- bz.s @getMenuIcon ; Otherwise, we’ll disable the item later.
-
- subq #4,sp
- move.w #128,-(sp)
- _GetPicture
- move.l (sp),DylanTalkGlobals.aboutPicture(a2) ; Save pointer to about box picture
- _DetachResource ; Detach the resource so it sticks around.
-
- ; Get our menu, and icons, and insert it in the menu.
-
- @getMenuIcon
- subq #4,sp
- move.l sp,a0 ; Space for the handle result
- subq #2,sp
- move.l a0,-(sp)
- _NewIconSuite ; Create a new icon suite
- addq #2,sp ; Ignore the error for now
- move.l (sp)+,d7 ; Get the handle to the new icon suite
- move.l d7,DylanTalkGlobals.dylanTalkIcons(a2)
-
- lea IconTypeTable,a4 ; Get list of icon types to put in the suite
- @loadIconLoop
- move.l (a4)+,d3 ; Get an icon type
- bz.s @registerGestaltSelector ; Got all the icons
-
- ; Get the icon resource
-
- subq #4,sp
- move.l d3,-(sp)
- move.w #kDylanTalkIconID,-(sp)
- _Get1Resource ; Get an icon resource
- move.l (sp)+,d4 ; Get the handle
- bz.s @loadIconLoop ; If no icon of this type, just skip it.
- move.l d4,-(sp)
- _DetachResource ; Detach it so it sticks around
-
- ; Add it to the icon suite.
-
- subq #2,sp
- move.l d4,-(sp) ; Handle to the icon resource
- move.l d7,-(sp) ; Handle to the icon suite
- move.l d3,-(sp) ; Resource type.
- _AddIconToSuite ; Add the icon to the icon suite
- addq #2,sp
- bra.s @loadIconLoop
-
- ; Register our gestalt selector and save our globals
-
- @registerGestaltSelector
- lea DylanTalkGestalt,a1
- move.l a3,a0
- _HUnlock
- move.l a0,2(a1) ; Save handle to globals in our gestalt routine
- subq #2,sp
- move.l #gestaltDylanTalk,d0 ; DylanTalk selector type
- move.l a1,a0 ; A1 still has pointer to Gestalt routine
- _NewGestalt
- addq #2,sp
-
- ; Open the default personality file
-
- @openPersonalityFile
- jsr OpenPersonalityFile
- move.l (sp)+,TheZone ; Restore the current zone
-
- rts
-
- IconTypeTable
- dc.l Small1BitMask, Small4BitData, Small8BitData, 0
- EndProc
-
- ;_________________________________________________________________________________________
- ; Gestalt routine
-
- DylanTalkGestalt Proc Export
- bra.s GestaltStart
- globalHandle
- ds.l 1 ; Handle to DylanTalk globals stored here
- GestaltStart
- move.l (sp)+,a0 ; Get return address
- move.l (sp)+,a1 ; Get address to stash global handle
- move.l globalHandle,(a1)
- addq #4,sp ; Pop selector
- clr.w (sp) ; Return noErr
- jmp (a0)
- EndProc
-
- ;_________________________________________________________________________________________
- ; SystemTask patch to install DylanTalk menu
- ;
-
- SystemTaskPatch Proc Export
- Import FillPersonalityMenu
-
- bra.s PatchStart
- PatchHeader oldSystemTaskPatch
-
- PatchStart
- movem.l a3/a4,-(sp)
-
- move.l #gestaltDylanTalk,d0
- _Gestalt ; Get our globals
- move.l (a0),a3
- move.l DylanTalkGlobals.dylanTalkIcons(a3),a3 ; Get handle to icon suite
-
- ; Insert the menu into the SystemMenuList.
-
- move.l TheZone,-(sp)
- move.l SysZone,TheZone
- subq #4,sp
- move.w #kDylanTalkMenuID,-(sp)
- pea DylanTalkMenuName
- _NewMenu ; Create a new menu
- move.l (sp),a4 ; Leave handle for InsertMenu
- clr.w -(sp)
- move.l (a4),a0
- move.w #$0501,menuData(a0) ; Special signal for icon title.
- move.l a3,menuData+2(a0) ; Stuff icon suite handle into menu
- _InsertMenu
-
- ; Look for Personality files in the Preferences folder
-
- move.l a4,-(sp)
- jsr FillPersonalityMenu
-
- lea SystemTaskPatch,a0
- move.w #$4EF9,(a0) ; Change branch to jmp to short circuit this patch
- move.l $6F4,a0 ; Get cache flush vector
- jsr (a0) ; Flush the caches
-
- move.l (sp)+,TheZone
- movem.l (sp)+,a3/a4
- bra.s SystemTaskPatch
- DylanTalkMenuName
- dc.l 'Dylan'
- EndProc
-
- ;_________________________________________________________________________________________
- ; SystemMenu patch to take care of choices in the DylanTalk menu
-
- SystemMenuPatch Proc Export
- Export oldSystemMenu
- Import ChooseNewPersonality
- Import DylanTalkAbout
-
- bra.s PatchStart
- PatchHeader oldSystemMenu
-
- PatchStart
- move.l 4(sp),d0 ; Get menu selection
- swap d0 ; Get menu id into low word
- cmp.w #kDylanTalkMenuID,d0 ; Is it the DylanTalk menu?
- bne.s oldSystemMenu ; No. Get on with SystemMenu
-
- ; The user picked something in the DylanTalk menu.
-
- move.l TheZone,-(sp)
- move.l SysZone,TheZone
-
- swap d0
- cmpi.w #1,d0 ; See if it’s the about item
- bne.s @chooseNewPersonality ; It’s a personality.
- jsr DylanTalkAbout ; User picked the about box.
- bra.s @backToCaller
-
- ; Switch to a new personality
-
- @chooseNewPersonality
- move.w d0,-(sp) ; Pass item number to ChooseNewPersonality
- subq #4,sp
- swap d0
- move.w d0,-(sp)
- _GetMHandle ; Get handle to DylanTalk menu
- jsr ChooseNewPersonality
- @backToCaller
- move.l (sp)+,TheZone
- rts
- EndProc
-
- ;_________________________________________________________________________________________
- ; ShowHide patch to trigger the sound
-
- ShowHidePatch Proc Export
- Export oldShowHide
- Import FindDialogInGlobals
- Import GetFirstItem
- Import SpeakStringSounds, KillStringSounds
-
- bra.s PatchStart
- PatchHeader oldShowHide
- PatchStart
- move.l 6(sp),a0 ; Get pointer to WindowRecord
- cmpi.w #kDialogKind,windowKind(a0) ; Is it a dialog?
- bne.s oldShowHide ; No. Don’t talk.
-
- ; Make sure it’s modal
-
- subq #2,sp
- move.l a0,-(sp)
- _GetWVariant ; Get the window variant
- move.w (sp)+,d0 ; Get variant
- cmpi.w #dBoxProc,d0 ; Is it modal?
- beq.s @isModal
- cmpi.w #movableDBoxProc,d0 ; Or a movable modal?
- bne.s oldShowHide
-
- ; This is the right type of window. Are we showing or hiding a window?
-
- @isModal
- tst.b 4(sp) ; Non zero for show
- bz.s @removeDialogFromList ; This dialog is being hidden. Remove it from the list
-
- ; This dialog is being shown. Make sure it’s not in the list yet.
-
- move.l 6(sp),a0 ; Get dialog pointer again
- bsr FindDialogInGlobals ; Is it in the list?
- bnz.s oldShowHide ; If it is, we already talked, so don’t talk again.
-
- ; This dialog is not in the list. Stick it in an empty spot
-
- bsr FindDialogInGlobals
- bnz.s @foundSlot ; If non-zero, we have a place
- pea #'Ran out of slots'
- _DebugStr
- bra.s oldShowHide
- @foundSlot
- move.l 6(sp),d0
- move.l d0,(a0) ; Save the dialog
- bra.s FindFirstStaticTextItem ; Go find the first static string
-
- ; This dialog is being hidden. Remove it from the list if it’s there
-
- @removeDialogFromList
- move.l 6(sp),a0 ; Get dialog pointer
- bsr FindDialogInGlobals ; Find it in our globals
- bz.s oldShowHide ; If it’s not there, just go on
- clr.l (a0) ; Otherwise, zap it from our list
-
- move.l a3,-(sp)
- move.l #gestaltDylanTalk,d0
- _Gestalt ; Get handle to globals
- move.l a0,a3 ; Save it
- _HLock ; Lock it down
- move.l (a0),-(sp) ; Pass a pointer to our globals
- jsr KillStringSounds ; Release memory used for string sounds for this dialog
- move.l a3,a0
- _HUnlock
-
- move.l (sp)+,a3
- bra.s oldShowHide
-
- ;_________________________________________________________________________________________
- ;
- ; This is a dialog we want to talk. Gather all the static text items, and parse the first one.
- ;
-
- FindFirstStaticTextItem
- movem.l a3/a4,-(sp)
- move.l d0,a4 ; Save pointer to dialog
-
- move.l #gestaltDylanTalk,d0
- _Gestalt ; Get handle to globals
- move.l a0,a3 ; Save pointer to globals
- _Hlock ; lock our globals
-
- ; Reset the string back to nothing for each new dialog
-
- move.l (a0),a0
- move.l DylanTalkGlobals.firstDialogString(a0),a0 ; Reset string
- clr.l (a0)
-
- ; Find the first static text string in this dialog
-
- move.l a4,-(sp) ; Pass the dialog pointer
- jsr GetFirstItem ; Find the first static text item
- move.l (a3),a0 ; Get pointer to our globals
- move.l DylanTalkGlobals.firstDialogString(a0),a1
- tst.b (a1) ; Make sure there’s a string
- bz.s @noStringToSay ; If not, skip the call to SpeakStringSounds
-
- ; Say the text.
-
- move.l TheZone,-(sp) ; Save the current zone
- move.l SysZone,TheZone ; Do all our work in the system zone
- move.l a0,-(sp) ; Pass pointer to globals
- jsr SpeakStringSounds ; Start talking
- move.l (sp)+,TheZone ; Restore the current zone
- @noStringToSay
- move.l a3,a0
- _HUnlock ; unlock the globals
-
- movem.l (sp)+,a3/a4
- bra oldShowHide
- EndProc
-
- ;_________________________________________________________________________________________
- ; NewDialog tail patch that forces a call to ShowHide if the dialog is becoming visible.
- ; FUNCTION NewDialog(wStorage: Ptr; 30(A7)
- ; boundsRect: Rect; 26(A7)
- ; title: Str255; 22(A7)
- ; visible: BOOLEAN; 20(A7)
- ; theProc: INTEGER; 18(A7)
- ; behind: WindowPtr; 14(A7)
- ; goAwayFlag: BOOLEAN; 12(A7)
- ; refCon: LongInt; 8(A7)
- ; items: Handle): DialogPtr; 4(A7)
-
- NewDialogPatch Proc Export
- Export oldNewDialog
-
- bra.s PatchStart
- PatchHeader oldNewDialog
- oldReturnAddress
- jmp $12345678
- PatchStart
- tst.b 20(sp) ; is this dialog gonna be visible?
- beq.s oldNewDialog ; no skip tail patch
-
- lea oldReturnAddress,a0 ; save the oldReturnAddress
- move.l (sp),2(a0)
-
- lea comeBack,a0 ; replace it with us! so we
- move.l a0,(sp) ; don't have to repush all the params!
- bra.s oldNewDialog
-
- ; At this point the DialogPtr is on the stack as a return result
- ; We pass it to ShowHide and then jmp to the old return address
-
- comeBack
- move.l (sp),-(sp) ; push the window for show hide
- move.b #1,-(sp) ; true means show it
- _ShowHide
- bra.s oldReturnAddress ; get outta here…
- EndProc
-
- ;_________________________________________________________________________________________
- ; HiliteControlPatch
- ;
- ; This patch is used to catch keystrokes that hilite buttons (like return for
- ; “OK”, and escape for “Cancel”. I know, I could have patched the modal dialog
- ; event filter, but I didn’t feel like it. This patch is short circuited by
- ; TrackControlPatch and DialogSelectPatch, which also hilite buttons, but they
- ; care of the speeh.
-
- HiliteControlPatch Proc Export
- Import SpeakStringSounds
- Export oldHiliteControl
- Export patchIsActive
-
- bra.s PatchStart
- PatchHeader oldHiliteControl
- patchIsActive
- dc.w 1 ; This patch starts out active
- PatchStart
- move.w patchIsActive,d0 ; Is this patch active?
- bz.s oldHiliteControl ; No.
- cmpi.w #inButton,4(sp) ; Check the control part that’s being hilited
- bne.s oldHiliteControl ; If it’s not highlighting a button, don’t say anything
-
- ; It’s a button, or radio control. Speak it’s title.
-
- movem.l a2/a3,-(sp)
- move.l #gestaltDylanTalk,d0
- _Gestalt
- move.l a0,a3
- _HLock
- move.l (a3),a0 ; Get pointer to our globals
- move.l DylanTalkGlobals.firstDialogString(a0),a2 ; Get pointer to string to speak
- move.l 4+4+4+2(sp),-(sp) ; Get the control handle
- move.l a2,-(sp)
- _GetCTitle ; Get title of the control
- tst.b (a2) ; Make sure there’s a title
- bz.s @doneSpeakingTitle ; If not, don’t say anything
-
- ; Say the title
-
- move.l TheZone,-(sp) ; Save the current zone
- move.l SysZone,TheZone ; Do all our work in the system zone
- move.l (a3),a0
- move.w #-1,DylanTalkGlobals.playingControlSound(a0) ; Mark that we’re talking about a button
- move.l a0,-(sp) ; Pass a pointer to the globals
- jsr SpeakStringSounds ; Go say something
- move.l (sp)+,TheZone ; Restore the previous zone
- @doneSpeakingTitle
- move.l a3,a0
- _HUnlock ; Unlock our globals
- movem.l (sp)+,a2/a3
- bra.s oldHiliteControl
- EndProc
-
- ;_________________________________________________________________________________________
- ; TrackControlPatch
- ;
- ; If the user lets up outside a button, check box, or radio button, say “Enh.”
- ;
-
- TrackControlPatch Proc Export
- Import SpeakStringSounds
- Import patchIsActive
- Export oldTrackControl
-
- TrackControlStack Record {RetAddr},Decr
- trackResult ds.w 1
- paramBegin equ *
- theControl ds.l 1
- startPt ds.l 1
- actionProc ds.l 1
- paramSize equ paramBegin - *
- retAddr ds.l 1
- EndR
-
- With TrackControlStack
- bra.s PatchStart
- PatchHeader oldTrackControl
- oldReturnAddress
- jmp $12345678
- savedControl
- ds.l 1
-
- PatchStart
- move.l theControl(sp),a1 ; Get the control handle
- move.l (a1),a0
- move.l contrlDefHandle(a0),a0 ; Get CDEF handle
- tst.l (a0) ; See if CDEF is loaded
- bnz.s @cdefIsLoaded ; Skip LoadResource if it is
- move.l a0,-(sp)
- _LoadResource ; Load the CDEF into memory
- @cdefIsLoaded
- move.l (a0),a0 ; Get pointer to CDEF
- tst.w 8(a0) ; Make sure it’s CDEF 0, since we’re only doing buttons, and such.
- bnz.s oldTrackControl ; If it’s not, skip out of here.
-
- lea patchIsActive,a0
- clr.w (a0) ; Disable the HiliteControl patch
- lea oldReturnAddress,a0 ; Get address to save real return address
- addq #2,a0 ; Skip past jump
- move.l (sp)+,(a0)+ ; Save the return address
- move.l a1,(a0) ; Save the control handle
- bsr.s oldTrackControl ; And call TrackControl
- lea patchIsActive,a0
- move.w #1,(a0) ; Reactivate the HiliteControl patch
-
- move.l a3,-(sp) ; We’ll need an extra register now
- move.l #gestaltDylanTalk,d0
- _Gestalt ; Get our globals
- _HLock ; Lock it down
- move.l a0,a3 ; Save it
- move.l (a3),a0 ; Get pointer to globals
-
- ; See where the mouse was let up.
-
- tst.w 4(sp) ; Check result
- bnz.s @dontSayAnything ; If inside, DialogSelectPatch will do the talking.
-
- ; If mouse was outside control, say “Enh.”
-
- move.l TheZone,-(sp)
- move.l SysZone,TheZone ; Do all our work in the system zone
- move.l DylanTalkGlobals.firstDialogString(a0),a1
- move.l EnhSoundName,(a1)
- move.l a0,-(sp) ; Pass pointer to globals
- jsr SpeakStringSounds ; Say the title
- move.l (sp)+,TheZone
- move.l a3,a0
- _HUnlock ; Unlock our globals
- @dontSayAnything
- move.l (sp)+,a3 ; Restore a3
- bra.s oldReturnAddress ; And return to the caller
-
- EnhSoundName
- dc.l 'Enh'
- EndProc
-
- ;_________________________________________________________________________________________
- ; DialogSelectPatch
- ;
- ; Catch hits on buttons, check buttons, and radio controls and speak the title of these
- ; controls.
- ;
-
- DialogSelectPatch Proc Export
- Export oldDialogSelect
- Import SpeakStringSounds
- Import patchIsActive
-
- DialogSelectStack Record {A6Link},Decr
- result ds.w 1
- paramBegin equ *
- theEvent ds.l 1
- theDialog ds.l 1
- itemHit ds.l 1
- paramSize equ paramBegin - *
- retAddr ds.l 1
- A6Link ds.l 1
- itemType ds.w 1
- itemHandle ds.l 1
- itemBox ds.l 2
- localSize equ *
- EndR
-
- With DialogSelectStack
- bra.s PatchStart
- PatchHeader oldDialogSelect
- PatchStart
- link a6,#localSize
-
- ; First, call DialogSelect to let the Dialog Manager take care of the event.
-
- lea patchIsActive,a0
- clr.w (a0) ; Disable HiliteControl patch
- clr.w result(a6)
- subq #2,sp
- move.l theEvent(a6),-(sp)
- move.l theDialog(a6),-(sp)
- move.l itemHit(a6),-(sp)
- bsr.s oldDialogSelect
- lea patchIsActive,a0
- move.w #1,(a0) ; Reenable HiliteControl patch
- move.b (sp)+,d0
- move.b d0,result(a6) ; Pass the result back.
- bz.s @exitDialogSelectPatch ; Just exit if no item was hit
-
- ; Find out the item type of the item that was hit.
-
- move.l theDialog(a6),a0
- move.l (a0),-(sp) ; Dialog that was hit
- move.l itemHit(a6),a0
- move.w (a0),-(sp) ; Item number that was hit
- pea itemType(a6)
- pea itemHandle(a6)
- pea itemBox(a6)
- _GetDItem ; Get info about this item
-
- cmpi.w #ctrlItem,itemType(a6)
- blt.s @exitDialogSelectPatch
- cmpi.w #ctrlItem+radCtrl,itemType(a6) ; Is it a button type?
- bgt.s @exitDialogSelectPatch ; If it’s not, just exit.
-
- ; It’s a button, radio control, or check box. Speak it’s title.
-
- movem.l a2/a3,-(sp)
- move.l #gestaltDylanTalk,d0
- _Gestalt
- move.l a0,a3
- _HLock
- move.l (a3),a0 ; Get pointer to our globals
- move.l DylanTalkGlobals.firstDialogString(a0),a2 ; Get pointer to string to speak
- move.l itemHandle(a6),-(sp)
- move.l a2,-(sp)
- _GetCTitle ; Get title of the control
- tst.b (a2) ; Make sure there’s a title
- bz.s @doneSpeakingTitle ; If not, don’t say anything
-
- ; Say the title
-
- move.l TheZone,-(sp) ; Save the current zone
- move.l SysZone,TheZone ; Do all our work in the system zone
- move.l (a3),a0
- move.w #-1,DylanTalkGlobals.playingControlSound(a0) ; Mark that we’re talking about a button
- move.l a0,-(sp) ; Pass a pointer to the globals
- jsr SpeakStringSounds ; Go say something
- move.l (sp)+,TheZone ; Restore the previous zone
- @doneSpeakingTitle
- move.l a3,a0
- _HUnlock ; Unlock our globals
- movem.l (sp)+,a2/a3
- @exitDialogSelectPatch
- unlk a6
- move.l (sp)+,a0
- add.w #paramSize,sp
- jmp (a0)
- EndProc
-
- ;_________________________________________________________________________________________
- ; FindCharacterRun(theString: StringPtr; offset: integer; stringToSpeak: StringPtr, Var runStartOffset: integer): Boolean;
- ;
- ; This routine picks out a run of alpha numeric characters from a string, starting at
- ; offset. If the end of the string is encountered before a run is found, false is
- ; returned. If a run is found, a copy of the run is made into stringToSpeak, and the
- ; offset at which this run began are returned, and the function result is true.
- ;
-
- FindCharacterRun Proc Export
- FindCharacterRunStack Record {A6Link},Decr
- result ds.w 1 ; Whether a run was found before the end of the string
- paramBegin equ *
- theString ds.l 1 ; String to search
- offset ds.w 1 ; Offset into string to start searching from
- stringToSpeak ds.l 1 ; String to copy run into.
- runStartOffset ds.l 1
- paramSize equ paramBegin - *
- retAddr ds.l 1
- A6Link ds.l 1
- isNumber ds.w 1
- localSize equ *
- EndR
-
- With FindCharacterRunStack
- link a6,#localSize
- movem.l d2/d3,-(sp)
-
- move.w #-1,result(a6) ; Assume we’ll find a run
- move.l theString(a6),a0 ; Get pointer to string
- moveq #0,d0
- moveq #0,d3
- move.b (a0)+,d0 ; Get length of string
- subq #1,d0
- move.w offset(a6),d1
- sub.w d1,d0 ; There will be offset less characters to run through before the end of the string
- ble.s @endOfString ; If this knocks us to the end, bail
- add.w d1,a0 ; Start at the desired offset
- move.l stringToSpeak(a6),a1 ; Get string to put run into
- clr.b (a1)+ ; Assum zero length string to start with
-
- clr.w isNumber(a6)
- moveq #0,d1 ; Count of run length
- moveq #0,d2
- @nextCharacter
- move.b (a0)+,d2 ; Get a character
- cmpi.b #'’',d2 ; Special curly apostrophe check
- beq.s @isAlphanumeric
- tst.w isNumber(a6) ; Are we in a number run?
- bz.s @skipCommaCheck ; If not, don’t bother with the comma
- cmpi.b #',',d2 ; Check for commas in a number run
- beq.s @isAlphanumeric ; Count the comma as part of the run for number runs.
- @skipCommaCheck
- cmpi.b #'.',d2 ; Do some special checking for a period
- bne.s @isNotPeriod
- @checkEndOfSentence
- tst.l d1 ; See if we’ve started a run yet.
- bz.s @isAlphanumeric ; If we’re just starting play some silence.
- cmpi.b #' ',(a0) ; See if the following character is a space
- beq.s @gotToEnd ; Pick up the period next time.
- tst.w isNumber(a6) ; If we’re in the middle of a word, see if the word is a number
- bnz.s @isAlphanumeric ; If we’re in a number, treat the period as part of the number
- bra.s @notAlphanumeric ; Otherwise, ignore the period.
- @isNotPeriod
- cmpi.b #'0',d2 ; Check the lower numeric bound
- blt.s @notAlphanumeric ; It’s not in range
- cmpi.b #'9',d2 ; Check the upper numeric bound
- bgt.s @notANumber ; It’s not a number. Try a letter
- move.w #1,isNumber(a6) ; Set our flag that says we’re doing a number run.
- bra.s @isAlphanumeric ; And remember this digit.
- @notANumber
- tst.w isNumber(a6) ; Are we in the middle of a number run?
- bnz.s @gotToEnd ; If we are, stop right here.
- cmpi.b #'A',d2 ; Check the lower alphabetic bound
- blt.s @notAlphanumeric ; It’s not a letter
- cmpi.b #'Z',d2 ; Check the upper alphabetic bound
- ble.s @isAlphanumeric ; It’s a letter
- @notAlphanumeric
- tst.l d1 ; Check the current run length
- bnz.s @gotToEnd ; Otherwise, we’ve found the end of the run
- addq #1,d3 ; Still looking for the start of a run. Bump the offset to the run’s start.
- bra.s @getNextCharacter ; If it’s zero, we’re still looking for the start of a run
- @isAlphanumeric
- move.b d2,(a1)+ ; Copy the character into speech buffer
- addq #1,d1 ; Increment run length at any rate
- @getNextCharacter
- dbra d0,@nextCharacter ; Check the next character
-
- ; Got to the end of the string. See if we found a run.
-
- tst.l d1 ; Check run length
- bnz.s @gotToEnd ; Found a run
- @endOfString
- clr.w result(a6) ; Say we didn’t find a run
- @gotToEnd
- move.l stringToSpeak(a6),a0 ; Get string to speak.
- move.b d1,(a0) ; Stuff length of run into string
- move.l runStartOffset(a6),a0
- move.w d3,(a0) ; Pass back where the run started.
-
- move.l (sp)+,d2
- unlk a6
- move.l (sp)+,a0
- add.w #paramSize,sp ; Pop parameters
- jmp (a0)
-
- ;_________________________________________________________________________________________
- ; This routine converts all lower case alphabetic characters into uppercase in a string.
- ;
-
- ConvertStringToUpperCase Proc Export
- move.l (sp)+,a0 ; Get return address
- move.l (sp)+,a1 ; Get string pointer
- move.l a0,-(sp) ; Put return address back on stack
-
- moveq #0,d0
- moveq #0,d1
- move.b (a1)+,d0 ; Get length of string
- bz.s @exitConvertString ; Bail if no string.
- subq #1,d0 ; Zero base it.
- @conversionLoop
- move.b (a1)+,d1 ; Get a character
- cmpi.b #'a',d1 ; Is it lower case?
- blt.s @nextCharacter ; No. Leave it alone
- subi.w #'a'-'A',d1 ; Convert it to uppercase
- move.b d1,-1(a1)
- @nextCharacter
- dbra d0,@conversionLoop
- @exitConvertString
- rts
- EndProc
-
- ;_________________________________________________________________________________________
- ; Look for a given dialog pointer in our globals.
- ;
- ; Parameters:
- ; A0 -> Pointer to dialog to search for
- ; A0 <- Address of dialog in global handle, or NIL if not found
- ;
-
- FindDialogInGlobals Proc Export
- move.l d7,-(sp)
- move.l a0,d7 ; Keep dialog pointer in a static register
- move.l #gestaltDylanTalk,d0
- _Gestalt ; Get handle to globals
- move.l (a0),a0
-
- ; Search for the given dialog pointer in globals
-
- moveq #kNumCachedDialogs - 1,d0
- @searchForDialog
- cmp.l (a0)+,d7 ; Is this one it?
- beq.s @foundDialog
- dbra d0,@searchForDialog
- move.l #4,a0 ; Didn’t find the dialog
- @foundDialog
- subq #4,a0 ; Back up to the right pointer
- move.l (sp)+,d7 ; Restore A4
- move.l a0,d0 ; Set the condition code
- rts
- EndProc
-
- ;_________________________________________________________________________________________
- ; Given a dialog pointer, find the first static text item.
-
- GetFirstItem Proc Export
- Import MungeInParamText
-
- GetFirstItemStackFrame Record {A6Link},Decr
- paramBegin equ *
- theDialog ds.l 1
- paramSize equ paramBegin - *
- retAddr ds.l 1
- A6Link ds.l 1
- potentialFirstItem ds.l 1
- itemType ds.w 1
- itemHandle ds.l 1
- itemBox ds.l 2
- itemString ds.b 256
- localSize equ *
- EndR
-
- With GetFirstItemStackFrame
- link a6,#localSize
- movem.l d6-d7/a2-a4,-(sp)
-
- move.l #gestaltDylanTalk,d0
- _Gestalt
- move.l (a0),a0 ; Get handle to globals
- move.l DylanTalkGlobals.firstDialogString(a0),d6 ; Get pointer to space to save string in.
-
- move.l theDialog(a6),a4 ; Keep dialog in a register
- move.l items(a4),a0 ; Get items handle
- move.l (a0),a0
- move.w (a0),d7 ; Get count of items - 1
- move.l #$7FFF7FFF,potentialFirstItem(a6) ; Start with a point that’s way down and to the right
-
- ; Loop through all the items in the dialog, looking for static text items
-
- @itemLoop
- move.l a4,-(sp) ; Dialog record
- move.w d7,-(sp)
- add.w #1,(sp) ; Item number is one based
- pea itemType(a6)
- pea itemHandle(a6)
- pea itemBox(a6)
- _GetDItem
-
- ; See if it’s a static text item
-
- move.w itemType(a6),d0 ; Get the type
- andi.w #$7F,d0 ; Mask off disabled bit
- cmpi.w #statText,d0 ; Is it a static text item?
- bne.s @nextItem
-
- ; It’s a static text item. Make sure it’s in the visible portion of the dialog.
-
- subq #2,sp
- pea portRect(a4) ; Compare the dialog rectangle
- pea itemBox(a6) ; Against the rectangle for this item
- pea itemBox(a6) ; And put the result in the item rectangle. If the item is visible, this will remain unchanged.
- _SectRect ; See if the two rectangles intersect.
- tst.b (sp)+ ; Is the text item visible?
- bz.s @nextItem ; If not, then don’t bother.
-
- ; Check the position of this item to see if it’s closer to the upper left of the dialog than previous text items.
-
- move.l potentialFirstItem(a6),d0 ; Get our candidate string up until now
- swap d0 ; Get top into low word
- cmp.w itemBox(a6),d0 ; Is the new one closer to the top of the dialog?
- blt.s @nextItem ; If not, skip it.
- bne.s @saveItemPosition ; If this text item is higher up, then go get the string
- swap d0 ; If it’s at the same level as the previous one, check the left edge
-
- ; Dean’s architectural integrity kicks in and tells him to look at the system justification
- ; to decide which side of the dialog is the front.
-
- tst.b TESysJust+1 ; Check the low byte like everyone else in the world
- bnz.s @isRightJustified
- cmp.w itemBox+left(a6),d0 ; Is the new one closer to the left of the dialog?
- blt.s @nextItem ; If the new one is further to the right, skip it.
- @saveItemPosition
- move.l itemBox(a6),potentialFirstItem(a6) ; This is the new candidate. Remember it’s coordinates
- bra.s @newCandidate
-
- @isRightJustified
- cmp.w itemBox+right(a6),d0
- bgt.s @nextItem
- move.w itemBox(a6),potentialFirstItem(a6)
- move.w itemBox+right,potentialFirstItem+left(a6)
-
- ; Get the string
-
- @newCandidate
- move.l itemHandle(a6),-(sp)
- pea itemString(a6)
- _GetIText
-
- subq #4,sp
- pea itemString(a6)
- bsr MungeInParamText ; Do ParamText subsitution
- move.l (sp)+,a0 ; Get handle to munged text
-
- ; Filled in all the subsitutions. Save the string
-
- move.l a0,a2 ; Save the handle
- move.l (a0),a0
- move.l d6,a1 ; Get pointer to previous string
- moveq #0,d0
- move.b (a0),d0
- addq #1,d0
- _BlockMove ; Copy in the new string
- move.l a2,a0
- _DisposeHandle ; Done with the munged string
- @nextItem
- dbra d7,@itemLoop
-
- movem.l (sp)+,d6-d7/a2-a4
- unlk a6
- move.l (sp)+,a0
- addq #paramSize,sp
- jmp (a0)
- EndProc
-
- ;_________________________________________________________________________________________
- ; Given a pointer to a static text string, convert it to a string handle, then munge
- ; in the paramtext strings in low memory. Pass back the handle to the munged string.
-
- MungeInParamText Proc Export
- MungeInParamTextStack Record {A6Link},Decr
- mungedTextHandle ds.l 1
- paramBegin equ *
- stringToMunge ds.l 1
- paramSize equ paramBegin - *
- retAddr ds.l 1
- A6Link ds.l 1
- currentCaret ds.w 1 ; Holder for “^[0-3]”
- localSize equ *
- EndR
-
- With MungeInParamTextStack
- link a6,#localSize
- movem.l d7/a2-a4,-(sp)
-
- move.l stringToMunge(a6),a0 ; Pointer to string to munge
- moveq #0,d0
- move.b (a0),d0 ; Get length
- addq #1,d0 ; Include length byte
- _NewHandle ,Sys ; Make a handle for munging
- move.l a0,a4 ; Keep it around
- move.l (a0),a1
- move.l stringToMunge(a6),a0
- move.b (a0),d0
- addq #1,d0
- _BlockMove ; Copy the string into the handle
-
- moveq #3,d7 ; Four paramtext strings
- lea DAStrings,a2 ; Get address of paramtext strings
- move.w #'^0',currentCaret(a6) ; Start with ^0
- @mungerLoop
- move.l (a2),d0 ; Get a replacement string
- bz.s @noReplacementString
- move.l d0,a0
- _HLock ; Lock down the replacement string
-
- move.l (a0),a3 ; Pointer to replacement string
- moveq #0,d0
- move.b (a3),d0 ; Get length byte
- addq #1,a3 ; Skip length
- @lookForAnotherCaret
- subq #4,sp
- move.l a4,-(sp) ; Handle to munge
- clr.l -(sp) ; Offset to start munging at
- pea currentCaret(a6) ; String to replace
- move.l #2,-(sp) ; It’s two bytes long
- move.l a3,-(sp) ; Replacement string
- move.l d0,-(sp) ; It’s length
- _Munger
- tst.l (sp)+ ; See if it found the caret
- bge.s @lookForAnotherCaret ; Make sure there are no other occurances of
-
- move.l (a2)+,a0
- _HUnlock ; Unlock the replacement string
- bra.s @nextSubsitution
- @noReplacementString
- addq #4,a2
- @nextSubsitution
- add.b #1,currentCaret+1(a6) ; Next ^ character
- dbra d7,@mungerLoop
-
- move.l a4,a0
- _GetHandleSize ; Find out length of munged string
- move.l (a0),a0
- move.b d0,(a0) ; Store the new length
- move.l a4,mungedTextHandle(a6) ; Pass back the result
-
- movem.l (sp)+,d7/a2-a4
- unlk a6
- move.l (sp)+,a0 ; Get return address
- addq #4,sp
- jmp (a0)
- EndProc
- End